Skip to content

Add Trigger functionality to Basler backend#81

Draft
C-Achard wants to merge 15 commits into
cy/limit-display-fpsfrom
cy/basler-trigger-config
Draft

Add Trigger functionality to Basler backend#81
C-Achard wants to merge 15 commits into
cy/limit-display-fpsfrom
cy/basler-trigger-config

Conversation

@C-Achard

@C-Achard C-Achard commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Scope

  • Adds functional config and API for setting up triggered Basler cameras
  • Adapt UI added in Add UI to configure Trigger mode #79 to be more backend agnostic and flexible
  • Tested with pypylon virtual cameras setup
  • Added tests for trigger mode in Basler backend
  • Finished implementing software trigger mode
  • Misc:
    • Tweaked the logging to be less frequent when followers are not triggered

Motivation

Adds the possibility to setup master/follower or software-controlled camera schemes to the Basler backend so as to obtain viable files for DLC3D or other experimental work.

@C-Achard C-Achard requested a review from Copilot June 19, 2026 19:44
@C-Achard C-Achard self-assigned this Jun 19, 2026
@C-Achard C-Achard added enhancement New feature or request camera Related to cameras and camera backends labels Jun 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the Basler camera backend with trigger configuration support (hardware trigger input + master output + early software-trigger hooks), and updates the trigger configuration UI to be backend-aware (Basler vs GenTL). It also attempts to reduce log spam when follower/external cameras are waiting for trigger pulses.

Changes:

  • Added Basler backend trigger configuration parsing and GenICam/pypylon feature configuration (plus timeout handling and best-effort restore on close).
  • Updated trigger settings dialog to use backend-specific UI profiles (show/hide relevant fields and provide backend-appropriate suggestions).
  • Introduced throttled logging for “waiting for hardware trigger” timeouts (currently implemented incorrectly; see comments).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
dlclivegui/services/multi_camera_controller.py Adds throttled logging for trigger-wait timeouts (but currently breaks at runtime due to method placement/state).
dlclivegui/gui/main_window.py Removes in-file pypylon emulation env var snippet from the main window module.
dlclivegui/gui/camera_config/trigger_config_dialog.py Makes the trigger dialog backend-aware via TriggerUiProfile and hides irrelevant fields for Basler/GenTL.
dlclivegui/gui/camera_config/camera_config_dialog.py Ensures default trigger config is created for Basler as well as GenTL.
dlclivegui/cameras/backends/basler_backend.py Implements Basler trigger settings parsing and configuration, adds trigger capabilities, and adjusts read timeouts for trigger-wait mode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dlclivegui/services/multi_camera_controller.py
Comment thread dlclivegui/services/multi_camera_controller.py Outdated
Comment on lines +18 to +26
# NOTE @C-Achard: This could be added in settings eventually
# Forces pypylon to create N emulation virtual cameras,
# mostly for testing. This should not be enabled for release.
ENABLE_PYLON_EMU = True
if ENABLE_PYLON_EMU:
import os

os.environ["PYLON_CAMEMU"] = "4"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open as reminder

Comment thread dlclivegui/cameras/backends/basler_backend.py
Comment thread dlclivegui/cameras/backends/basler_backend.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Comment thread dlclivegui/config.py
Comment on lines +25 to +28
## Debug
### Timing logs
SINGLE_CAMERA_WORKER_DO_LOG_TIMING: bool = True
MULTI_CAMERA_WORKER_DO_LOG_TIMING: bool = True
Comment thread dlclivegui/cameras/backends/basler_backend.py Outdated
Comment thread dlclivegui/cameras/backends/basler_backend.py
Comment thread dlclivegui/services/multi_camera_controller.py
Comment thread dlclivegui/services/multi_camera_controller.py
C-Achard added 9 commits July 14, 2026 10:43
Implement comprehensive trigger support for the Basler backend: import CameraTriggerSettings, parse trigger config (roles: off, external/follower, software, master), and persist trigger_actual into the namespace. Add trigger configuration helpers (_configure_trigger*, _resolve_trigger_source, _restore_trigger_idle), software trigger execution (trigger_once), and many feature/enum/numeric helper methods with debug logging. Make RetrieveResult use a configurable _retrieve_timeout_ms (derived from trigger.timeout) and limit it for hardware-triggered cameras to allow prompt shutdown; raise a TimeoutError when waiting for hardware triggers. Expose hardware_trigger capability as BEST_EFFORT and add an env var-based pylon emulation toggle for testing. Misc: add debug dumps of trigger-related nodes and best-effort restore of trigger state on close.
Delete commented-out code that forced pypylon to create emulation virtual cameras (PYLON_CAMEMU), which was only intended for testing and should not be enabled for release. Also remove an extraneous blank line to tidy up the file.
Introduce a TriggerUiProfile dataclass and trigger_ui_profile_for_backend() to drive dialog presentation per backend. Replace free-text source field with an editable QComboBox providing backend suggestions and defaults. Add profile-driven visibility/enabling for input, master, software and strobe/line output fields, plus helper methods to manage form rows and combo text. Only include strobe-related payload fields when the backend profile exposes them. Misc: expand info/help text, show backend in group title, increase dialog min-width, refine tooltips, and improve model↔UI mapping and payload construction.
Previously the default trigger configuration was only applied for backend 'gentl' and always stored under the 'gentl' properties key. This change treats the backend name dynamically (accepting both 'gentl' and 'basler'), and stores the default trigger settings under the actual backend key in cam.properties. Also preserves behavior when cam.properties or the backend namespace is not a dict.
Reduce log flooding when waiting for hardware triggers by adding throttled logging.

Changes in dlclivegui/services/multi_camera_controller.py:
- Import time.
- Add a new _log_interval_while_waiting_for_trigger_s attribute to SingleCameraWorker.
- Replace the direct LOGGER.debug call for expected trigger wait timeouts with a call to _log_trigger_wait_throttled.
- Implement _log_trigger_wait_throttled to suppress repeated timeout messages, emit a consolidated debug message, and report how many repeated logs were suppressed.

This prevents high-frequency expected poll-timeout logs (common in trigger-waiting modes) from overwhelming the logs.
Extend the test conftest FakePylon to better emulate pypylon: add FakePylonTimeoutException, richer _Feature (symbolics, min/max/inc, read/write checks, call tracking), _EnumEntry, expanded _DeviceInfo, GrabResult.release tracking, and a more complete InstantCamera (timeouts, software trigger, trigger/line features, buffer and grab controls, test knobs). Reset the fake factory and provide default fake devices and a basler_settings_factory fixture. Patch the basler SDK fixture to use FakePylon. Add new test suite tests/cameras/backends/test_basler_backend.py covering lifecycle (open/read/close, fast-start, idempotent close), discovery/rebind, resolution/exposure/gain/fps handling, and comprehensive trigger behavior (follower/master/software/external) to validate backend logic.
Add throttled logging for hardware-trigger wait timeouts in SingleCameraWorker to avoid noisy repeated timeout messages. Introduce _trigger_wait_log_interval, _last_trigger_wait_log and _trigger_wait_suppressed_count and move _log_trigger_wait_throttled into the worker; remove the duplicate implementation from MultiCameraController. Also mark the Basler software-trigger test as xfail because software trigger support is not implemented yet.
Integrates optional WorkerTimingStats into the Basler camera backend and refactors FPS handling and frame retrieval. Adds a _configure_frame_rate() helper to centralize AcquisitionFrameRate enabling, setting and readbacks (logs many related nodes and records actual_fps). Initializes a WorkerTimingStats instance (controlled by SINGLE_CAMERA_WORKER_DO_LOG_TIMING) and wraps RetrieveResult/convert/get array/release steps with timing measurements, improved error handling, proper grab_result release on exceptions, and frame counting/logging. Overall improves observability and robustness when setting frame rates and reading frames from Basler cameras.
@C-Achard C-Achard force-pushed the cy/basler-trigger-config branch from 4b27757 to 783c1f0 Compare July 14, 2026 08:45
@C-Achard C-Achard requested a review from deruyter92 July 15, 2026 08:13
C-Achard added 3 commits July 16, 2026 10:59
Refactors `MultiCameraController.stop()` to track a dedicated stopping state and finalize shutdown only after all camera threads are actually stopped. It now delays `all_stopped` emission until cleanup is truly complete, keeps references to threads that refuse to terminate, and avoids the previous premature "stopped" state. The main window now explicitly calls `stop(wait=True)` during camera/project shutdown so UI transitions block until camera teardown is done.
Import `genicam` alongside `pylon` and add a helper to detect `genicam.TimeoutException` safely. The hardware-trigger timeout path now converts exceptions to `TimeoutError` only for true SDK timeout exceptions, avoiding incorrect timeout handling for unrelated grab failures.
C-Achard added 2 commits July 16, 2026 11:15
Add coverage for hardware-triggered Basler reads to ensure SDK timeout errors are re-raised as `TimeoutError` with the expected message and exception cause. Update the Basler SDK test patching to also inject a fake `genicam.TimeoutException`, so timeout handling paths can be exercised reliably in tests.
Tightens controller start/stop state handling to prevent unsafe restarts while shutdown is in progress, and finalizes shutdown only after all camera threads actually exit. Cleanup now resets runtime state consistently, `all_stopped` emission is guarded against duplicates, and stop requests only fan out to workers when actively running. Camera stop handling was also refined so full initialization failure and last-camera runtime shutdown both transition into the same safe finalization path, with clearer logging around incomplete shutdowns.
@C-Achard C-Achard changed the base branch from cy/gentl-trigger-ui to cy/limit-display-fps July 16, 2026 09:54
@C-Achard C-Achard requested a review from Copilot July 16, 2026 09:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Comment on lines +19 to +26
# NOTE @C-Achard: This could be added in settings eventually
# Forces pypylon to create N emulation virtual cameras,
# mostly for testing. This should not be enabled for release.
ENABLE_PYLON_EMU = True
if ENABLE_PYLON_EMU:
import os

os.environ["PYLON_CAMEMU"] = "4"
return requested, True

if requested.lower() == "auto":
for candidate in ("Line1", "Line2", "Line3", "Line4", "Line0", "Software", "Action1"):
Comment on lines 471 to 475
self._workers.clear()
self._threads.clear()
self._settings.clear()
self._runtime_info.clear()
self._started_cameras.clear()
self._failed_cameras.clear()
Comment on lines +474 to +476
class FakePylonTimeout(Exception):
pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

camera Related to cameras and camera backends enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants